package com.macaline.controller;


import com.google.common.util.concurrent.RateLimiter;
import com.macaline.bean.Result;
import com.macaline.controller.base.BaseController;
import com.macaline.entity.Seckill;
import com.macaline.service.base.BaseService;
import com.macaline.service.impl.SeckillServiceImpl;
import com.macaline.technology.rabbitMq.RabbitMqUtil;
import com.rabbitmq.client.*;
import com.xnx3.DateUtil;
import lombok.extern.log4j.Log4j2;
import net.sf.json.JSONObject;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Log4j2
@RestController
@RequestMapping("/seckill")
public class SeckillController extends BaseController<Seckill> {


    public SeckillController() {
        asynMq();

    }

    @Resource
    private SeckillServiceImpl seckillServie;

    @Resource
    private RedisTemplate redisTemplate;


    public String QUEUE_NAME = "SECKILL_QUEUE";
    public String PRODUCT_NUM = "productNum";
    public String PRODUCT_NUM_LOCK = "productNumLock";
    List<String> userList = new LinkedList<String>();
    {
        userList.add("AA");
        userList.add("BB");
        userList.add("CC");
        userList.add("DD");
        userList.add("EE");
        userList.add("FF");
        userList.add("GG");
        userList.add("HH");
    }

    Random random = new Random();




    @Override

    public BaseService<Seckill> getService() {
        return seckillServie;
    }





    final RateLimiter rateLimiter = RateLimiter.create(10); // 表示每秒产生的令牌数
    ExecutorService executorService = Executors.newFixedThreadPool(5); // 一池多线程


    /**
     * 库存减一
     *
     * @param seckill
     * @return
     */
    @PostMapping("/reduceNum")
    public Result reduceNum(@RequestBody Seckill seckill) throws InterruptedException, IOException, TimeoutException {
        // 未使用MQ
        boolean b = rateLimiter.tryAcquire(1, TimeUnit.SECONDS);
        if(!b){
//            System.out.println("限流中断");
//            return Result.error("限流中断");
        }

        // 随机用户信息
        int i = random.nextInt(userList.size());
        String userName = userList.get(i);

        // 如果已经保存用户的访问记录了，说明规定时间内重复点击了，该请求被丢掉
        if(redisTemplate.opsForValue().get("seckill:" + userName + ":shopId") != null){
            System.out.println(userName + "用户秒杀请求重复");
//            return Result.error(userName + "用户已参与秒杀");
        }
        // 添加用户访问标识，3秒内访问无效
        System.out.println(userName + "用户秒杀请求成功:" + DateUtil.currentDate(DateUtil.FORMAT_DEFAULT));
        redisTemplate.opsForValue().set("seckill:" + userName + ":shopId", "1", 3, TimeUnit.SECONDS);

        Boolean boo = redisTemplate.opsForValue().setIfAbsent(PRODUCT_NUM_LOCK, 1);
        if(boo){
            System.out.println("未获取到redis锁");
            return Result.error("未获取到redis锁");
        }

        // 查询redis缓存中的库存
        Object o = redisTemplate.opsForValue().get(PRODUCT_NUM);
        int productNum = Integer.parseInt(o.toString());
        if(productNum < 1){
            redisTemplate.delete(PRODUCT_NUM_LOCK);
            return Result.error("库存不足");
        }


        System.out.println("redis存库"+ productNum);
        // 商品数量减一
        redisTemplate.opsForValue().set(PRODUCT_NUM, productNum - 1);
        // 移除锁
        redisTemplate.delete(PRODUCT_NUM_LOCK);

//        return seckillServie.reduceNum(seckill);

        // 发送异步MQ消息
        RabbitMqUtil.sendMessage(QUEUE_NAME, JSONObject.fromObject(seckill).toString());
        return Result.success();
    }

    public void asynMq(){
        String Nack = "nack:";
        new Thread(() -> {
            Connection connection = null;
            try {
                connection = RabbitMqUtil.getConnection("SECKILL_CONNECTION");
                Channel channel = connection.createChannel();
                channel.queueDeclare(QUEUE_NAME, true, false, false, null);
                DefaultConsumer consumer = new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                        String msg = new String(body);
                        Seckill seckill = (Seckill) JSONObject.toBean(JSONObject.fromObject(msg), Seckill.class);
                        // 确认消费
                        try {
                            Object o = com.macaline.technology.redis.util.RedisTemplate.getObject(Nack + seckill.getUuid());
                            if(o != null){
                                System.out.println("MQ二次消费消息ID：" + seckill.getUuid());
                            }
                            Result result = seckillServie.reduceNum(seckill);
                            if(result.isSuc()){
                                System.out.println("MQ消费成功,ID:" + seckill.getUuid());
                                channel.basicAck(envelope.getDeliveryTag(),false);
                                com.macaline.technology.redis.util.RedisTemplate.delteObject(Nack + seckill.getUuid());
                            }else {
                                // 消费失败 进行重新排序
                                com.macaline.technology.redis.util.RedisTemplate.setObject(Nack + seckill.getUuid(), 1);
                                System.out.println("MQ消费失败原因： " + result.getInfo() +" 进行重新排序,ID:" + seckill.getUuid());
                                channel.basicNack(envelope.getDeliveryTag(),false, true);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                };
                channel.basicConsume(QUEUE_NAME, false, consumer);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "MQ_CONSUER").start();
    }


    /**
     * 库存减一
     *
     * @return
     */
    @PostMapping("/getCount")
    public Result getCount() {
        return seckillServie.getCount();
    }

    /**
     * 库存减一
     * @return
     */
    @PostMapping("/refreshCount")
    public Result refreshCount() {
        redisTemplate.opsForValue().set(PRODUCT_NUM, 10);
        redisTemplate.delete(PRODUCT_NUM_LOCK);
        Seckill s = new Seckill();
        s.setId(1);
        s.setNum(10);
        s.setUpdateNum(0);
        seckillServie.update(s);
        return seckillServie.refreshCount();
    }

}
